home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Routines to read and write mail and news headers. The code here
- ** is gross and complicated, and it would be nice if somebody rewrote
- ** it to be clean and simple, especially the CrackFrom routine.
- */
- #include "gate.h"
- #include <time.h>
- #if defined(RCSID)
- static char RCS[] =
- "$Header: /nfs/papaya/u2/rsalz/src/newsgate/RCS/rfc822.c,v 1.11 91/07/22 10:26:51 rsalz Exp $";
- #endif /* defined(RCSID) */
-
- #if defined(HAVE_TIMEB)
- #include <sys/timeb.h>
- #else
- struct timeb {
- time_t time;
- unsigned short millitm;
- short timezone;
- short dstflag;
- };
- #endif /* defined(HAVE_TIMEB) */
-
- extern time_t time();
- extern char *asctime();
- extern struct tm *gmtime();
-
- #define QUESTIONABLE(c) \
- ((c) == '"' || (c) == '(' || (c) == ')' || (c) == '\\')
-
- #define HDR_OTHER -1
- #define HDR_END FALSE
- #define HDR_APPROVED 1
- #define HDR_CONTROL 2
- #define HDR_DATE 3
- #define HDR_DISTRIBUTION 4
- #define HDR_EXPIRE 5
- #define HDR_FOLLOWTO 6
- #define HDR_FROM 7
- #define HDR_KEYWORDS 8
- #define HDR_MESSAGEID 9
- #define HDR_NEWSGROUP 10
- #define HDR_ORGANIZATION 11
- #define HDR_REFERENCES 12
- #define HDR_REPLYTO 13
- #define HDR_SENDER 14
- #define HDR_SUMMARY 15
- #define HDR_TITLE 16
-
-
- /*
- ** The list of headers we recognize; all others are stripped.
- */
- typedef struct _HTYPE {
- char *Name;
- int Type;
- } HTYPE;
-
- STATIC HTYPE HeaderList[] = {
- { "Approved:", HDR_APPROVED },
- { "Control:", HDR_CONTROL },
- { "Date:", HDR_DATE },
- { "Posted:", HDR_DATE },
- { "Distribution:", HDR_DISTRIBUTION },
- { "Expires:", HDR_EXPIRE },
- { "Followup-To:", HDR_FOLLOWTO },
- { "From:", HDR_FROM },
- { "Keywords:", HDR_KEYWORDS },
- { "Message-ID:", HDR_MESSAGEID },
- { "Newsgroups:", HDR_NEWSGROUP },
- { "Organization:", HDR_ORGANIZATION },
- { "In-Reply-To:", HDR_REFERENCES },
- { "References:", HDR_REFERENCES },
- { "Reply-To:", HDR_REPLYTO },
- { "Sender:", HDR_SENDER },
- { "Summary:", HDR_SUMMARY },
- { "Subject:", HDR_TITLE },
- { "Title:", HDR_TITLE },
- };
-
-
-
- /*
- ** Getline is like fgets, but deals with continuation lines. It also
- ** ensures that even if a line that is too long is received, the
- ** remainder of the line is thrown away instead of treated like a second
- ** line.
- */
- STATIC char *
- Getline(buf, len, fp)
- char *buf;
- int len;
- FILE *fp;
- {
- register char *cp;
- register int c;
- register int n;
-
- for (n = 0, cp = buf; n < len && (c = getc(fp)) != EOF && c != '\n'; )
- if (!iscntrl(c) || c == '\b' || c == '\t') {
- *cp++ = c;
- n++;
- }
- if (c == EOF && cp == buf)
- return NULL;
- *cp = '\0';
-
- if (c != '\n')
- /* Line too long - read part didn't fit into a newline */
- while ((c = getc(fp)) != '\n' && c != EOF)
- continue;
- else if (cp == buf) {
- /* Don't look for continuation of blank lines */
- *cp++ = '\n';
- *cp = '\0';
- return buf;
- }
-
- while ((c = getc(fp)) == ' ' || c == '\t') {
- /* Continuation line. */
- if ((n += 2) < len) {
- *cp++ = '\n';
- *cp++ = c;
- }
- while ((c = getc(fp)) != '\n' && c != EOF)
- if ((!iscntrl(c) || c == '\b' || c == '\t') && n++ < len)
- *cp++ = c;
- }
- if (n >= len - 1)
- cp = buf + len - 2;
- *cp++ = '\n';
- *cp = '\0';
- if (c != EOF)
- /* push back first char of next header */
- (void)ungetc(c, fp);
- return buf;
- }
-
-
- /*
- ** I guess this is basically strncasecmp
- */
- STATIC int
- prefix(full, pref)
- register char *full;
- register char *pref;
- {
- register char fc;
- register char pc;
-
- while ((pc = *pref++) != '\0') {
- fc = *full++;
- if (isupper(fc))
- fc = tolower(fc);
- if (isupper(pc))
- pc = tolower(pc);
- if (fc != pc)
- return FALSE;
- }
- return TRUE;
- }
-
-
- STATIC int
- HeaderType(p)
- register char *p;
- {
- static int save = HDR_END;
- register HTYPE *hp;
- char *colon;
- char *space;
-
- /* some consistency checks (i.e. is this really a header line?) */
- if (p == NULL || !isascii(*p) || *p == '\n')
- return save = HDR_END;
-
- if (WHITE(*p))
- /* Continuation line. */
- return save;
-
- /* If we don't get a "<no space> <colon> <space>", it's not a header. */
- if ((colon = IDX(p, ':')) == NULL
- || ((space = IDX(p, ' ')) && space < colon))
- return save = HDR_END;
-
- for (hp = HeaderList; hp < ENDOF(HeaderList); hp++)
- if (prefix(p, hp->Name))
- return save = hp->Type;
- return save = HDR_OTHER;
- }
-
-
- /*
- ** Get the contents of the field of the header line, appending it, with a
- ** space delimeter if it's a continuation line. If there is already
- ** something in the header storage, skip this header line and the
- ** continuations.
- */
- STATIC void
- getfield(src, dest, size)
- register char *src;
- register char *dest;
- register int size;
- {
- static int skip = FALSE;
- register char *p;
-
- if (src == NULL || dest == NULL)
- return;
-
- if (WHITE(*src)) {
- /* Continuation line. If skipping or no room, ignore. */
- if (skip || (size -= strlen(dest)) <= 0)
- return;
- /* Munch all but one whitespace, append it to header. */
- while (*src && WHITE(*src))
- src++;
- *--src = ' ';
- (void)strncat(dest, src, size - 1);
- }
- else {
- skip = FALSE;
- if (*dest) {
- /* Already got a value, so mark this as one to skip. */
- skip = TRUE;
- return;
- }
- if ((src = IDX(src, ':')) == NULL)
- /* Can't happen! */
- return;
- /* Skip colon, eat whitespace. */
- for (src++; *src && WHITE(*src); )
- src++;
- (void)strncpy(dest, src, size - 1);
- }
-
- /* Munch trailing whitespace. */
- for (p = dest + strlen(dest); --p >= dest && (*p == '\n' || WHITE(*p)); )
- continue;
- p[1] = '\0';
- }
-
-
- STATIC time_t
- cgtdate(datestr)
- char *datestr;
- {
- static time_t lasttime;
- static char save[SM_SIZE];
- char junk[40];
- char month[40];
- char day[30];
- char tod[60];
- char year[50];
- char buf[SM_SIZE];
- #if defined(USE_GETABSDATE)
- extern time_t getabsdate();
- #else
- extern time_t getdate();
- #endif /* defined(USE_GETABSDATE) */
-
- if (save[0] && EQ(datestr, save))
- return lasttime;
-
- #if defined(USE_GETABSDATE)
- lasttime = getabsdate(datestr, (struct timeb *)NULL);
- #else
- lasttime = getdate(datestr, (struct timeb *)NULL);
- #endif /* defined(USE_GETABSDATE) */
-
- if (lasttime < 0 &&
- sscanf(datestr, "%s %s %s %s %s", junk, month, day, tod, year) == 5) {
- (void)sprintf(buf, "%s %s, %s %s", month, day, year, tod);
- #if defined(USE_GETABSDATE)
- lasttime = getabsdate(buf, (struct timeb *)NULL);
- #else
- lasttime = getdate(buf, (struct timeb *)NULL);
- #endif /* defined(USE_GETABSDATE) */
- }
- (void)strncpy(save, datestr, sizeof save);
- return lasttime;
- }
-
-
- /*
- ** Print the date in ARPA format, not ctime(3) format.
- */
- STATIC void
- DoDate(fp, ud)
- FILE *fp;
- register char *ud;
- {
- register char *p;
- register char *q;
- register int i;
- char buff[SM_SIZE];
-
- q = buff;
-
- #if 0
- /* until every site installs the fix to getdate.y, the day
- * of the week can cause time warps */
- /* "Mon, " */
- p = &ud[0]; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ','; *q++ = ' ';
- #endif /* 0 */
-
- /* "16" */
- p = &ud[8];
- if (*p == ' ')
- p++;
- else
- *q++ = *p++;
- *q++ = *p++; *q++ = ' ';
-
- /* "Sep " */
- p = &ud[4]; *q++ = *p++; *q++ = *p++; *q++ = *p++; *q++ = ' ';
-
- /* "79 " */
- p = &ud[22]; *q++ = *p++; *q++ = *p++; *q++ = ' ';
-
- /* "01:03:52" */
- for (p = &ud[11], i = 8; i > 0; i--)
- *q++ = *p++;
-
- /* " GMT" */
- *q++ = ' '; *q++ = 'G'; *q++ = 'M'; *q++ = 'T'; *q = '\0';
-
- Fprintf(fp, "Date: %s\n", buff);
- }
-
-
- /*
- ** Crack an RFC822 from header field into address and fullname. We do
- ** this to make sure we write things out in official form. "Be liberal
- ** in what you accept, conservative in what you generate." Anyhow, we
- ** read things into three buffers, one for all <...> text, one for all
- ** (...) text, and a third for stuff not in either. Either the first or
- ** third buffer will be the real address, depending on whether there is
- ** anything in buffer two or not.
- */
- int
- CrackFrom(addr, name, p)
- ADDRCHAR *addr;
- char *name;
- char *p;
- {
- register char *adp;
- register char *bp;
- register ADDRCHAR *ap;
- register ADDRCHAR *cp;
- register ADDRCHAR CurrChar;
- register int comment;
- register int address;
- register int addrfound;
- register int QuoteNext;
- register int InQuotes;
- ADDRCHAR *xp;
- ADDRCHAR comm[LG_SIZE];
- char commbuf[LG_SIZE];
- char addrbuf[LG_SIZE];
-
- /* Just to make sure. */
- *name = '\0';
- *addr = '\0';
-
- if (p == NULL)
- return FALSE;
-
- /* Eat leading white space. */
- while (*p && isspace(*p))
- p++;
-
- /* Set defaults. Start with an allocated copy of a comment string. */
- comm[0] = '\0';
- commbuf[0] = '\0';
- addrbuf[0] = '\0';
- adp = addrbuf;
- comment = 0;
- addrfound = 0;
- address = 0;
- QuoteNext = 0;
- InQuotes = 0;
- for (; *p; p++) {
- CurrChar = (*p & UNQUOTE_MASK) | QuoteNext;
- QuoteNext = CurrChar == '\\' ? QUOTE_MASK : 0;
- if (!comment && CurrChar == '"')
- InQuotes = InQuotes ? 0 : QUOTE_MASK;
- else
- CurrChar |= InQuotes;
- switch (CurrChar) {
- case '(':
- if (comment == 0) {
- cp = commbuf;
- *cp = '\0';
- }
- comment++;
- break;
- case ')':
- if (comment > 0 && --comment == 0) {
- *cp = '\0';
- xp = comm;
- if (*xp) {
- while (*xp)
- xp++;
- *xp++ = ',';
- *xp++ = ' ';
- }
- for (cp = &commbuf[1]; (*xp++ = *cp) != '\0'; cp++)
- continue;
- cp = NULL;
- continue;
- }
- break;
- case '<':
- if (address)
- return FALSE; /* AWK! Abort! */
- if (!comment) {
- address++;
- *adp = '\0';
- adp = addr;
- }
- break;
- case '>':
- if (!comment && address) {
- address--;
- addrfound++;
- *adp = '\0';
- for (adp = addrbuf; *adp; )
- adp++;
- continue;
- }
- break;
- }
-
- if (comment)
- *cp++ = CurrChar;
- else if (!address || CurrChar != '<')
- *adp++ = CurrChar;
- if (*p == '\0')
- break;
- }
-
- *adp++ = '\0';
-
- if (addrfound) {
- for (ap = name, xp = addrbuf; (*ap++ = (*xp & UNQUOTE_MASK)) != 0; xp++)
- continue;
- }
- else {
- for (cp = addr, xp = addrbuf; (*cp++ = *xp) != 0; xp++)
- continue;
- *name = '\0';
- }
-
- /* Just to be sure that we got the full name, we'll take all of
- * the comments. */
- if (*comm) {
- ap = name;
- if (*ap) {
- while (*ap)
- ap++;
- *ap++ = ',';
- *ap++ = ' ';
- }
- for (xp = comm; (*ap = (*xp++ & UNQUOTE_MASK)) != 0; ap++)
- continue;
- }
- /* Copy back, skipping leading spaces and trailing spaces. */
- for (ap = addr, cp = addr; isspace(*cp); cp++)
- continue;
- while ((*ap = *cp++) != 0)
- ap++;
- while (*addr && isspace(*--ap))
- *ap = '\0';
-
- /* Since characters in (comments) are interpreted differently from
- * those in "phrase" <address>, play it safe and remove all
- * questionable characters. */
- for (ap = name, bp = name; isspace(*bp) || QUESTIONABLE(*bp); bp++)
- continue;
- while ((*ap = *bp++) != '\0')
- if (!QUESTIONABLE(*ap))
- ap++;
- while (*name && isspace(*--ap))
- *ap = '\0';
- return TRUE;
- }
-
-
- /*
- ** Write out an RFC822 header, paying no attention to line limits.
- ** Ideally, we should do continuations in here...
- */
- int
- rfc822write(hp, fp)
- register HBUF *hp;
- register FILE *fp;
- {
- time_t t;
-
- if (hp->path[0])
- Fprintf(fp, "Path: %s\n", hp->path);
- if (hp->from[0])
- Fprintf(fp, "From: %s\n", hp->from);
- if (hp->nbuf[0])
- Fprintf(fp, "Newsgroups: %s\n", hp->nbuf);
- if (hp->title[0])
- Fprintf(fp, "Subject: %s\n", hp->title);
- if (hp->ident[0])
- Fprintf(fp, "Message-ID: %s\n", hp->ident);
- /* Get current time. This will be used resolve the timezone. */
- if (hp->subdate[0] == '\0')
- (void)time(&t);
- else if ((t = cgtdate(hp->subdate)) < 0) {
- Fprintf(fp, "X-Unparseable-Date: %s\n", hp->subdate);
- (void)time(&t);
- }
- DoDate(fp, asctime(gmtime(&t)));
- if (hp->expdate[0])
- Fprintf(fp, "Expires: %s\n", hp->expdate);
- if (hp->followid[0])
- Fprintf(fp, "References: %s\n", hp->followid);
- if (hp->ctlmsg[0])
- Fprintf(fp, "Control: %s\n", hp->ctlmsg);
- if (hp->sender[0])
- Fprintf(fp, "Sender: %s\n", hp->sender);
- if (hp->replyto[0])
- Fprintf(fp, "Reply-To: %s\n", hp->replyto);
- if (hp->followto[0])
- Fprintf(fp, "Followup-To: %s\n", hp->followto);
- if (hp->distribution[0])
- Fprintf(fp, "Distribution: %s\n", hp->distribution);
- if (hp->organization[0])
- Fprintf(fp, "Organization: %s\n", hp->organization);
- if (hp->keywords[0])
- Fprintf(fp, "Keywords: %s\n", hp->keywords);
- if (hp->summary[0])
- Fprintf(fp, "Summary: %s\n", hp->summary);
- if (hp->approved[0])
- Fprintf(fp, "Approved: %s\n", hp->approved);
- Fprintf(fp, "\n");
- return !ferror(fp);
- }
-
-
- rfc822read(hp, fp, buff, buffsize)
- register HBUF *hp;
- register FILE *fp;
- char *buff;
- int buffsize;
- {
- register int i;
- long curpos;
-
- /* Zap out the headers. */
- hp->approved[0] = '\0';
- hp->ctlmsg[0] = '\0';
- hp->subdate[0] = '\0';
- hp->distribution[0] = '\0';
- hp->expdate[0] = '\0';
- hp->followto[0] = '\0';
- hp->from[0] = '\0';
- hp->followid[0] = '\0';
- hp->keywords[0] = '\0';
- hp->ident[0] = '\0';
- hp->nbuf[0] = '\0';
- hp->organization[0] = '\0';
- hp->title[0] = '\0';
- hp->replyto[0] = '\0';
- hp->summary[0] = '\0';
- hp->path[0] = '\0';
- hp->sender[0] = '\0';
-
- i = HeaderType(buff);
- do {
- curpos = ftell(fp);
- switch (i) {
- case HDR_APPROVED:
- getfield(buff, hp->approved, sizeof hp->approved);
- break;
- case HDR_CONTROL:
- getfield(buff, hp->ctlmsg, sizeof hp->ctlmsg);
- break;
- case HDR_DATE:
- getfield(buff, hp->subdate, sizeof hp->subdate);
- break;
- case HDR_DISTRIBUTION:
- getfield(buff, hp->distribution, sizeof hp->distribution);
- break;
- case HDR_EXPIRE:
- getfield(buff, hp->expdate, sizeof hp->expdate);
- break;
- case HDR_FOLLOWTO:
- getfield(buff, hp->followto, sizeof hp->followto);
- break;
- case HDR_FROM:
- getfield(buff, hp->from, sizeof hp->from);
- break;
- case HDR_KEYWORDS:
- getfield(buff, hp->keywords, sizeof hp->keywords);
- break;
- case HDR_MESSAGEID:
- getfield(buff, hp->ident, sizeof hp->ident);
- break;
- case HDR_NEWSGROUP:
- getfield(buff, hp->nbuf, sizeof hp->nbuf);
- break;
- case HDR_ORGANIZATION:
- getfield(buff, hp->organization, sizeof hp->organization);
- break;
- case HDR_REFERENCES:
- getfield(buff, hp->followid, sizeof hp->followid);
- break;
- case HDR_REPLYTO:
- getfield(buff, hp->replyto, sizeof hp->replyto);
- break;
- case HDR_SENDER:
- getfield(buff, hp->sender, sizeof hp->sender);
- break;
- case HDR_SUMMARY:
- getfield(buff, hp->summary, sizeof hp->summary);
- break;
- case HDR_TITLE:
- getfield(buff, hp->title, sizeof hp->title);
- break;
- }
- } while ((i = HeaderType(Getline(buff, buffsize, fp))) != HDR_END);
-
- if (*buff != '\n')
- (void)fseek(fp, curpos, 0);
- }
-